#include <cctype>
#include <cstring>
#include <iostream>

#include "MIFparser.hpp"
#include "Analyst.hpp"

using namespace ModuleEMapData;
using namespace std;

#define strnicmp( a, b, len ) strncasecmp( (a), (b), (len) )

static Analyst lexical;
static set< primitive_attr > global_attr_set;

// 确定了当前处理的是什么图元之后调用这些函数来解析图元的数据
void parse_point         ( ifstream&, primitive&, Token& ) throw(StreamError);
void parse_line          ( ifstream&, primitive&, Token& ) throw(StreamError);
void parse_polyline      ( ifstream&, primitive&, Token& ) throw(StreamError);
void parse_region        ( ifstream&, primitive&, Token& ) throw(StreamError);
void parse_arc           ( ifstream&, primitive&, Token& ) throw(StreamError);
void parse_text          ( ifstream&, primitive&, Token& ) throw(StreamError);
void parse_rect          ( ifstream&, primitive&, Token& ) throw(StreamError);
void parse_roundrect     ( ifstream&, primitive&, Token& ) throw(StreamError);
void parse_ellipse       ( ifstream&, primitive&, Token& ) throw(StreamError);


inline bool is_primitive( Token& _t )
{
  return _t.token_type <= PRIMITIVE_MAXTYPE;
}

// 在map_file传递进来前，在外层检查map_file的有效性
MIFparser::MIFparser( ifstream& map_file ) throw(StreamError)
  :ifile( map_file ), cur_token( INVALID_TYPE )
{
  primitive_loader[ PRIMITIVE_POINT     ] = parse_point;
  primitive_loader[ PRIMITIVE_LINE      ] = parse_line;
  primitive_loader[ PRIMITIVE_POLYLINE  ] = parse_polyline;
  primitive_loader[ PRIMITIVE_REGION    ] = parse_region;
  primitive_loader[ PRIMITIVE_ARC       ] = parse_arc;
  primitive_loader[ PRIMITIVE_TEXT      ] = parse_text;
  primitive_loader[ PRIMITIVE_RECT      ] = parse_rect;
  primitive_loader[ PRIMITIVE_ROUNDRECT ] = parse_roundrect;
  primitive_loader[ PRIMITIVE_ELLIPSE   ] = parse_ellipse;

  if( !parse_head() )		// 文件格式不对
    {
      throw StreamError( StreamError::UNVALID_FILE_FORMAT );
    }
}

MIFparser::~MIFparser( void )
{
  global_attr_set.clear();
}

// 如果当前没有正在处理的行就说明的
bool MIFparser::eof( void ) const
{
  return ifile.eof( );
}

bool MIFparser::parse_head( void )
{
  // mif文件的文件头记录的是mid的数据库信息，这里不想太麻烦，不做mid的部分，所以
  // 不解析文件头，只是审查一下是不是符合mif的文件格式
  char cur_line[ LINE_MAX_BYTES ];
  ifile.getline( cur_line, LINE_MAX_BYTES );
  if ( 0 != strnicmp( cur_line, "VERSION", 7 ) ) // 7 == strlen("VERSION")
    {
      return false;
    }

  ifile.getline( cur_line, LINE_MAX_BYTES );
  if ( 0 != strnicmp( cur_line, "Charset", 7 ) ) // 7 == strlen("Charset")
    {
      return false;
    }
  // 以上两项通过就相当远是检查通过了，虽然这个方法很不严格

  // 吃掉文件头部分直到遇到DATA，然后读入下一行，返回
  while( !ifile.eof() )
    {
      ifile.getline( cur_line, LINE_MAX_BYTES );
      if ( 0 == strnicmp( cur_line, "DATA", 4 ) && !ifile.eof() )
	{
	  cur_token = lexical.get_token( ifile );
	  break;
	}
    }

  return true;
}

MIFparser& MIFparser::operator>>( primitive& p ) throw(StreamError)
{
  if( is_primitive( cur_token ) )
    {
      p.type = cur_token.token_type;
      primitive_loader[ cur_token.token_type ]( ifile, p, cur_token );
    }
  else if( ifile.eof() )
    {
      throw StreamError( StreamError::END_OF_STREAM );
    }
  else
    {
      throw StreamError( StreamError::UNVALID_FILE_FORMAT );
    }

  return *this;
}

// 点的解析
void parse_point( ifstream &ifile, primitive& primi, Token &cur_token ) throw(StreamError)
{
  // 点的数据是这样组织的
  // 
  // POINT x y
  // SYMBOL(shape,color,size)  --此行为可选

  // 设置一些由point规定的参数
  primi.coord_count = 1;
  primi.coord_data = new coord[1];
  primi.other_data = NULL;

  // 此时应该有记号流流到point处，我们期望下一个记号是整数
  cur_token = lexical.get_token( ifile );
  if( cur_token.token_type == NUMERAL_VALUE )
    {
      primi.coord_data[ 0 ].x = cur_token.value.fvalue;
    }
  else
    {
      throw StreamError( StreamError::UNVALID_FILE_FORMAT );
    }
  cur_token = lexical.get_token( ifile );
  if( cur_token.token_type == NUMERAL_VALUE )
    {
      primi.coord_data[ 0 ].y = cur_token.value.fvalue;
    }
  else
    {
      throw StreamError( StreamError::UNVALID_FILE_FORMAT );
    }
  //完成第一行


  cur_token = lexical.get_token( ifile );
  if( cur_token.token_type == KEYWORD_SYMBOL ) // 是symbol属性
    {
      primitive_attr attr;
      memset( &attr, 0, sizeof(attr) );
      attr.attr_name = SYMBOL_POINT;

      char buf[32];
      int shape, color, size;
      if( !ifile.eof() ){
	ifile.getline( buf, 32 );
      }else{
	throw StreamError( StreamError::UNVALID_FILE_FORMAT );
      }
      sscanf( buf, "(%d,%d,%d)", &shape, &color, &size );

      attr.attr_value[ 0 ] = shape;
      attr.attr_value[ 1 ] = color;
      attr.attr_value[ 2 ] = size;

      global_attr_set.insert( attr );

      primi.attribute[ 0 ] = global_attr_set.find( attr );

      cur_token = lexical.get_token( ifile );
    }
}

// 线段的解析
void parse_line( ifstream &ifile, primitive &primi, Token &cur_token ) throw(StreamError)
{
  // 线段的数组的组织形式
  // 
  // LINE x1 y1 x2 y2
  // PEN(width,pattern,color)  --此可选

  // 这个时候应该有cur_token -> "LINE"
  primi.coord_count = 2;
  primi.coord_data = new coord[ 2 ];
  primi.other_data = NULL;

  // 此时应该有记号流流到point处，我们期望下一个记号是整数
  for( int i=0; i < primi.coord_count; ++i )
    {
      cur_token = lexical.get_token( ifile );
      if( cur_token.token_type == NUMERAL_VALUE )
	{
	  primi.coord_data[ i ].x = cur_token.value.fvalue;
	}
      else
	{
	  throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	}
      cur_token = lexical.get_token( ifile );
      if( cur_token.token_type == NUMERAL_VALUE )
	{
	  primi.coord_data[ i ].y = cur_token.value.fvalue;
	}
      else
	{
	  throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	}
    }
  //完成第一行


  cur_token = lexical.get_token( ifile );
  if( cur_token.token_type == KEYWORD_PEN ) // 是pen属性
    {
      primitive_attr attr;
      memset( &attr, 0, sizeof(attr) );
      attr.attr_name = PEN;

      char buf[32];
      int shape, pattern, color;
      if( !ifile.eof() ){
	ifile.getline( buf, 32 );
      }else{
	throw StreamError( StreamError::UNVALID_FILE_FORMAT );
      }
      sscanf( buf, "(%d,%d,%d)", &shape, &pattern, &color );

      attr.attr_value[ 0 ] = shape;
      attr.attr_value[ 1 ] = pattern;
      attr.attr_value[ 2 ] = color;

      global_attr_set.insert( attr );

      primi.attribute[ 0 ] = global_attr_set.find( attr );

      cur_token = lexical.get_token( ifile );
    }
}

// 多边形解析
void parse_polyline( ifstream &ifile, primitive &primi, Token &cur_token ) throw(StreamError)
{
  // 多边形的数据组织形式

  // PLINE num
  // x y
  // ...
  // PEN(width,pattern,color) --可选
  // SMOOTH                   --可选
  primi.other_data = NULL;

  // 同样是期望下一个整数
  cur_token = lexical.get_token( ifile );
  if( cur_token.token_type == INTEGER_VALUE )
    {
      primi.coord_count = cur_token.value.ivalue;
    }
  else
    {
      throw StreamError( StreamError::UNVALID_FILE_FORMAT );
    }
  primi.coord_data = new coord[ primi.coord_count ];
  // 完成一行

  for( int i=0; i < primi.coord_count; ++i )
    {
      cur_token = lexical.get_token( ifile );
      if( cur_token.token_type == NUMERAL_VALUE )
	{
	  primi.coord_data[ i ].x = cur_token.value.fvalue;
	}
      else
	{
	  throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	}
      cur_token = lexical.get_token( ifile );
      if( cur_token.token_type == NUMERAL_VALUE )
	{
	  primi.coord_data[ i ].y = cur_token.value.fvalue;
	}
      else
	{
	  throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	}
    }
  // 坐标部分完成

  // 可选部分
  cur_token = lexical.get_token( ifile );
  primitive_attr attr;
  int current_attr_idx = 0;
  while( cur_token.token_type == KEYWORD_PEN || cur_token.token_type == KEYWORD_SMOOTH )
    {
      memset( &attr, 0, sizeof(attr) );
      // smooth属性
      if( cur_token.token_type == KEYWORD_SMOOTH )
	{
	  attr.attr_name = SMOOTH;
	  global_attr_set.insert( attr );
	  primi.attribute[ current_attr_idx++ ] = global_attr_set.find( attr );
	}
      else			// pen属性，上面有过代码了
	{
	  attr.attr_name = PEN;

	  char buf[32];
	  int shape, pattern, color;
	  if( !ifile.eof() ){
	    ifile.getline( buf, 32 );
	  }else{
	    throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	  }
	  sscanf( buf, "(%d,%d,%d)", &shape, &pattern, &color );

	  attr.attr_value[ 0 ] = shape;
	  attr.attr_value[ 1 ] = pattern;
	  attr.attr_value[ 2 ] = color;

	  global_attr_set.insert( attr );

	  primi.attribute[ current_attr_idx++ ] = global_attr_set.find( attr );
	}

      cur_token = lexical.get_token( ifile );
    }
}

void parse_region( ifstream& ifile, primitive& primi, Token &cur_token ) throw(StreamError)
{
  // 区域数据组织形式

  // REGION numreg
  //  reg_1_numpts
  // x y
  // ...
  //  reg_2_numpts  --如果numreg大于1那就一定有后面这些
  // x y
  // ...
  // pen(width,pattern,color) --可选
  // brush(pattern,forecolor,backcolor) --可选
  // center x y --可选

  // 由于一个primitive结构只存储一组点列，但是区域可
  // 能会有多个点列，所以使用other_data字段做成一个链表

  int numreg;
  // 现今应该是cur_token->REGION
  cur_token = lexical.get_token( ifile );
  if( cur_token.token_type == INTEGER_VALUE )
    {
      numreg = cur_token.value.ivalue;
    }

  // 取得一组点列
  primitive *p = &primi;
  for(int i=0; i < numreg; ++i )
    {
      // 期望下一个是numpts，获取此小块的点的数目
      cur_token = lexical.get_token( ifile );
      if( cur_token.token_type == INTEGER_VALUE )
	{
	  p->coord_count = cur_token.value.ivalue;
	}
      else
	{
	  cerr << __LINE__ << endl;
	  throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	}
      p->coord_data = new coord[ p->coord_count ];

      // 取得coord_count个坐标数据
      for( int j=0; j < p->coord_count; ++j )
	{
	  cur_token = lexical.get_token( ifile );
	  if( cur_token.token_type == NUMERAL_VALUE )
	    {
	      p->coord_data[ j ].x = cur_token.value.fvalue;
	    }
	  else
	    {
	      char x[40];
	      ifile.getline(x, 40);
	      cout << x << endl;
	      throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	    }
	  cur_token = lexical.get_token( ifile );
	  if( cur_token.token_type == NUMERAL_VALUE )
	    {
	      p->coord_data[ j ].y = cur_token.value.fvalue;
	    }
	  else
	    {
	      cerr << __LINE__ << endl;
	      throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	    }
	}
      // 完成一个点列

      if( i+1 == numreg )		// 完成所有点列
	{
	  p->other_data = NULL;
	}
      else			// 未完成那就进行链表接续
	{
	  p->other_data = static_cast<void*>(new primitive);
	  p = static_cast<primitive*>( p->other_data );
	}
    }
  // 完成所有点列

  // 处理可选属性
  // 可选部分
  cur_token = lexical.get_token( ifile );
  primitive_attr attr;
  int current_attr_idx = 0;
  while( cur_token.token_type == KEYWORD_PEN ||
	 cur_token.token_type == KEYWORD_BRUSH ||
	 cur_token.token_type == KEYWORD_CENTER )
    {
      memset( &attr, 0, sizeof(attr) );
      if( cur_token.token_type == KEYWORD_PEN )	// pen属性，上面有过代码了
	{
	  attr.attr_name = PEN;

	  char buf[32];
	  int shape, pattern, color;
	  if( !ifile.eof() ){
	    ifile.getline( buf, 32 );
	  }else{
	    throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	  }
	  sscanf( buf, "(%d,%d,%d)", &shape, &pattern, &color );

	  attr.attr_value[ 0 ] = shape;
	  attr.attr_value[ 1 ] = pattern;
	  attr.attr_value[ 2 ] = color;

	  global_attr_set.insert( attr );

	  primi.attribute[ current_attr_idx++ ] = global_attr_set.find( attr );
	}
      else if( cur_token.token_type == KEYWORD_BRUSH ) // brush属性
	{
	  attr.attr_name = BRUSH;

	  char buf[32];
	  int pattern, forecolor, backcolor;
	  if( !ifile.eof() ){
	    ifile.getline( buf, 32 );
	  }else{
	    throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	  }
	  sscanf( buf, "(%d,%d,%d)", &pattern, &forecolor, &backcolor );

	  attr.attr_value[ 0 ] = pattern;
	  attr.attr_value[ 1 ] = forecolor;
	  attr.attr_value[ 2 ] = backcolor;

	  global_attr_set.insert( attr );

	  primi.attribute[ current_attr_idx++ ] = global_attr_set.find( attr );
	}
      else			// center属性
	{
	  attr.attr_name = CENTER;

	  cur_token = lexical.get_token( ifile );
	  if( cur_token.token_type == NUMERAL_VALUE )
	    {
	      attr.attr_value[ 0 ] = reinterpret_cast<uint32_t&>( cur_token.value.fvalue );
	    }
	  else
	    {
	      throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	    }
	  cur_token = lexical.get_token( ifile );
	  if( cur_token.token_type == NUMERAL_VALUE )
	    {
	      attr.attr_value[ 1 ] = reinterpret_cast<uint32_t&>( cur_token.value.fvalue );
	    }
	  else
	    {
	      throw StreamError( StreamError::UNVALID_FILE_FORMAT );
	    }

	  global_attr_set.insert( attr );

	  primi.attribute[ current_attr_idx++ ] = global_attr_set.find( attr );
	}

      cur_token = lexical.get_token( ifile );
    }
}

void parse_arc( ifstream& ifile, primitive& primi, Token &cur_token ) throw(StreamError)
{
}

void parse_text( ifstream& ifile, primitive& primi, Token &cur_token ) throw(StreamError)
{
}

void parse_rect( ifstream& ifile, primitive& primi, Token &cur_token ) throw(StreamError)
{
}

void parse_roundrect( ifstream& ifile, primitive& primi, Token &cur_token ) throw(StreamError)
{
}

void parse_ellipse( ifstream& ifile, primitive& primi, Token &cur_token ) throw(StreamError)
{
}
